﻿using System;
using System.Transactions;

namespace TypeBuilderFx.Core.Emitters
{
    /// <summary>Options for dac emitters (in general).</summary>
    public abstract class DacOptions : SupportBaseClassTypeEmitterOptions
    {
        private string _connectionStringName = string.Empty;

        /// <summary>Default constructor.  Initialize default values.</summary>
        public DacOptions()
        {
            ConnectionMode = ConnectionMode.ConnectionStringName;
            TransactionScopeOption = TransactionScopeOption.Required;
            IsolationLevel = IsolationLevel.Serializable;
            CommandTimeout = TimeSpan.FromSeconds(30);
            TransactionTimeout = TimeSpan.FromSeconds(30);
        }

        /// <summary>Gets or sets the <see cref="ConnectionMode"/> of the DAC.</summary>
        /// <remarks>Default is <see cref="ConnectionMode.ConnectionStringName"/>.</remarks>
        public ConnectionMode ConnectionMode { get; set; }

        /// <summary>
        /// Name of the connection string in the configuration file.  Used only if <see cref="ConnectionMode"/> is set to
        /// <see cref="ConnectionMode.ConnectionStringName"/>.
        /// </summary>
        public string ConnectionStringName
        {
            get { return _connectionStringName; }
            set
            {
                if (value == null)
                {
                    _connectionStringName = string.Empty;
                }
                else
                {
                    _connectionStringName = value.Trim();
                }
            }
        }

        /// <summary>Gets or sets the <see cref="TransactionScopeOption"/>.</summary>
        /// <remarks>Default is <see cref="TransactionScopeOption.Required"/>.</remarks>
        public TransactionScopeOption TransactionScopeOption { get; set; }

        /// <summary>Gets or sets the isolation level of the transaction.</summary>
        /// <remarks>Default is <see cref="IsolationLevel.Serializable"/>.</remarks>
        public IsolationLevel IsolationLevel { get; set; }

        /// <summary>Gets or sets the timeout period for the command.</summary>
        /// <remarks>Default is 30 seconds.</remarks>
        public TimeSpan CommandTimeout { get; set; }

        /// <summary>Gets or sets the timeout period for the transaction.</summary>
        /// <remarks>Default is 30 seconds.</remarks>
        public TimeSpan TransactionTimeout { get; set; }

        #region object Methods
        /// <summary>Must be overriden by derived classes in order to be able to use as a key in an dictionary.</summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return ConnectionMode.GetHashCode()
                ^ TransactionScopeOption.GetHashCode()
                ^ IsolationLevel.GetHashCode()
                ^ CommandTimeout.GetHashCode()
                ^ TransactionTimeout.GetHashCode()
                ^ ConnectionStringName.GetHashCode()
                ^ base.GetHashCode();
        }

        /// <summary>Must be overriden by derived classes in order to be able to use as a key in an dictionary.</summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            DacOptions options = obj as DacOptions;

            return options != null
                && base.Equals(obj)
                && ConnectionMode == options.ConnectionMode
                && TransactionScopeOption == options.TransactionScopeOption
                && IsolationLevel == options.IsolationLevel
                && CommandTimeout == options.CommandTimeout
                && TransactionTimeout == options.TransactionTimeout
                && ConnectionStringName == options.ConnectionStringName;
        }
        #endregion

        /// <summary>
        /// Validates <see cref="ConnectionStringName"/> is or isn't set depending on the value of
        /// <see cref="ConnectionMode"/>.
        /// </summary>
        public override void Validate()
        {
            base.Validate();

            if (ConnectionMode == ConnectionMode.ConnectionStringName && ConnectionStringName == string.Empty)
            {
                throw new ArgumentNullException("ConnectionStringName");
            }
            if (ConnectionMode == ConnectionMode.ValidConnection && ConnectionStringName != string.Empty)
            {
                throw new ArgumentException("Shouldn't have a value", "ConnectionStringName");
            }
        }
    }
}